#include "sys.h"

#include "conn.h"
#include "debug.h"
#include "interface.h"
#include "conf.h"

//#define SNDTIMEO 100
//#define RCVTIMEO 100
#define SNDBUF_SIZE 1024
#define RCVBUF_SIZE 1024


extern ctx_t g_ctx;

/*	block ==1  block
  *	block ==0  noblock
*/
int setblock(int sock, int block) {
	int rc = -1;
	int flags = fcntl(sock, F_GETFL, NULL);
        if (flags >= 0) {
            if (block) {
                flags &= ~O_NONBLOCK; // clear nonblocking
            } else {
                flags |= O_NONBLOCK;  // set nonblocking
            }

            if (fcntl(sock, F_SETFL, flags) >= 0) {
                rc = 0;
            }
        }

}

conn_t* active_connect(int id){
		struct sockaddr_in 	remote_addr;
		conn_t* conn;
		int sockfd;
		int i;
		int node_idx = -1;
		int rc;

		for(i=0; i<g_ctx.conf.node_num; i++){
				if(g_ctx.conf.node[i].id == id){
						remote_addr = g_ctx.conf.node[i].addr;
						node_idx = i;
						break;
				}
		}

		if(node_idx== -1){
			debug_err("can't find this server id[%d]\n", id);
			return NULL;
		}

		if((sockfd = socket(PF_INET,SOCK_STREAM,0))<0){
			  debug_err(" create socket failed! errno[%d]\n", errno);
			  return NULL;
		}

		debug_info("active connect remote host fd[%d], addr:%s\n", sockfd, dump_addr(remote_addr) );

		sock_general_init(sockfd);
		conn = create_conn(node_idx, sockfd,remote_addr);
		assert(conn!=NULL);
		conn->conn_type = ACTIVE;
		conn->conn_state = CLOSED;
		
		conn_tab_add(conn);

		rc = connect(sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr));
		if(rc == -1){
			switch(errno){
				case ECONNREFUSED:
				case ENETUNREACH:
					debug_warn("add reconnect ! errno:%d\n", errno);
					add_reconn(conn);
					break;
				default:
					debug_err("connect failed! errno:%d\n", errno);
					 break;
			}
		}else{
			setblock(sockfd, 0);
			add_event(conn);
		}
		
		return conn;

}


int start_server(int server_id) {
    int rval;
    int i;
    struct epoll_event ev;
    node_t* node;

    node = find_node_from_id(server_id);
    assert(node!=NULL);
    g_ctx.server_addr= node->addr;	
    
    g_ctx.listenfd = socket(AF_INET, SOCK_STREAM, 0);
    check(g_ctx.listenfd);
  
    setblock(g_ctx.listenfd,0);
	set_reuseaddr(g_ctx.listenfd);

    rval = bind(g_ctx.listenfd, (struct sockaddr *) &g_ctx.server_addr, 
				sizeof(g_ctx.server_addr));
    check(rval);


    rval = listen(g_ctx.listenfd, LISTENQ);
    check(rval);

    g_ctx.epfd = epoll_create(MAX_EVENT);

    ev.data.fd = g_ctx.listenfd;
    ev.events = EPOLLIN | EPOLLOUT |EPOLLET;
    rval =epoll_ctl(g_ctx.epfd, EPOLL_CTL_ADD, g_ctx.listenfd, &ev);
    check(rval);

}



int set_reuseaddr(int sockfd){
	int rc;
	int opt = 1;
	rc = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(opt));
    check(rc);

}
		
int sock_general_init (int sockfd)
{
	struct timeval tv;
	int option;
	struct linger linger;
	int rc;

	//set send and recv timeout
	/* for unblock socket ,it doesn't need
	tv.tv_sec = SNDTIMEO;
	tv.tv_usec = 0;
	rc = setsockopt(sockfd, SOL_SOCKET,SO_SNDTIMEO, (void*)&tv, sizeof(tv));
    check(rc);
 	tv.tv_sec = RCVTIMEO;
	rc = setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(void *)&tv,sizeof(tv));
	check(rc);
	*/
	
    //set send buf 
    option = SNDBUF_SIZE;
    rc = setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,(void*)&option,sizeof(option));
	check(rc);
	//set recv buf
    option = RCVBUF_SIZE;
    rc = setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,(void *)&option,sizeof(option));
	check(rc);
	
	/*
    option = 1;
    rc = setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(void *)&option,sizeof(option));
	check(rc);
    */
	  
    // close linger

    linger.l_onoff = 0;
    linger.l_linger = 0;
    rc = setsockopt(sockfd,SOL_SOCKET,SO_LINGER,(void *)&linger,sizeof(linger));
	check(rc);

	//set keepalive
	option = 1;
    rc = setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char *)&option,sizeof(option));
	check(rc);


	//set keep idle
	/*
    option = KEEP_IDLE;
    rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (char *)&option, sizeof(option));
	check(rc);
    */

	//set keep intval
	/*
    option = AMP_KEEP_INTVL;
    rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (char *)&option, sizeof(option));
	check(rc);
    */
	//kep keep count
	/*
    option = AMP_KEEP_COUNT;
    rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (char *)&option, sizeof(option));
    check(rc);   
	*/            
}










